/**
 *  CoverArt.cpp
 *
 *  Copyright (C) 2008  David Andrs <pda@jasnapaka.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "StdAfx.h"
#include "mLasq.h"

#include "../share/UIHelper.h"
#include "CoverArt.h"

extern "C" {
#include "../libjpeg/jpeglib.h"
}

#ifdef MYDEBUG
#undef THIS_FILE
static TCHAR THIS_FILE[] = _T(__FILE__);
#include "crtdbg.h"
#define new MYDEBUG_NEW
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCaption

CCoverArt::CCoverArt() {
	GotCoverArt = FALSE;
}

CCoverArt::~CCoverArt() {
	m_bmpCoverArt.DeleteObject();
}


BEGIN_MESSAGE_MAP(CCoverArt, CStatic)
	//{{AFX_MSG_MAP(CCoverArt)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCoverArt message handlers

void CCoverArt::OnPaint() {
	CPaintDC dc(this); // device context for painting

	CRect rc;
	GetClientRect(rc);

	if (GotCoverArt) {
		CDC dcMem;
		dcMem.CreateCompatibleDC(&dc);
		CBitmap *oldBmp = dcMem.SelectObject(&m_bmpCoverArt);

		// resize bitmap
		dc.StretchBlt(0, 0, rc.Width(), rc.Height(), &dcMem, 0, 0, BmpWd, BmpHt, SRCCOPY);

		dcMem.SelectObject(oldBmp);
		dcMem.DeleteDC();
	}
	else {
		// TODO: display missing cover art
		dc.FillSolidRect(rc, ::GetSysColor(COLOR_WINDOW));
	}

	ValidateRect(NULL);

	// Do not call CStatic::OnPaint() for painting messages
}

BOOL CCoverArt::ReadJpeg(LPCTSTR fileName, CBitmap &bmp) {
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JDIMENSION num_scanlines;
	FILE *input_file;

	// Initialize the JPEG decompression object with default error handling.
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);

	if ((input_file = _wfopen(fileName, L"rb")) == NULL)
		return FALSE;

	// Specify data source for decompression
	jpeg_stdio_src(&cinfo, input_file);
	// Read file header, set default decompression parameters
	jpeg_read_header(&cinfo, TRUE);

	jvirt_sarray_ptr whole_image;	/* needed to reverse row order */
	JDIMENSION data_width;			/* JSAMPLEs per row */
	JDIMENSION row_width;			/* physical width of one row in the BMP file */
	JDIMENSION cur_output_row;		/* next row# to write to virtual array */
	JSAMPARRAY buffer;
	JDIMENSION buffer_height;

	// Calculate output image dimensions so we can allocate space
	jpeg_calc_output_dimensions(&cinfo);

	// Determine width of rows in the BMP file (padded to 4-byte boundary).
	row_width = cinfo.output_width * cinfo.output_components;
	data_width = row_width;
	while ((row_width & 1) != 0) row_width++;
	int pad_bytes = (int) (row_width - data_width);

	// Allocate space for inversion array, prepare for write pass
	whole_image = (*cinfo.mem->request_virt_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, FALSE, row_width, cinfo.output_height, (JDIMENSION) 1);
	cur_output_row = 0;

	// Create decompressor output buffer.
	buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1);
	buffer_height = 1;

	// Start decompressor
	jpeg_start_decompress(&cinfo);

	// save bitmap dimensions
	BmpWd = cinfo.output_width;
	BmpHt = cinfo.output_height;

	unsigned char *bits = new unsigned char[row_width * cinfo.output_height];
	register JSAMPROW outptr = bits + 0;

	// Process data
	while (cinfo.output_scanline < cinfo.output_height) {
		num_scanlines = jpeg_read_scanlines(&cinfo, buffer, buffer_height);
		register JSAMPROW inptr;
		register JDIMENSION col;

		// Transfer data.  Note destination values must be in BGR order
		// (even though Microsoft's own documents say the opposite).
		inptr = buffer[0];
		for (col = cinfo.output_width; col > 0; col--) {
			outptr[2] = *inptr++;	/* can omit GETJSAMPLE() safely */
			outptr[1] = *inptr++;
			outptr[0] = *inptr++;
			outptr += 3;
		}

		int pad = pad_bytes;
		while (--pad >= 0)
			*outptr++ = 0;
	}

	bmp.CreateBitmap(cinfo.output_width, cinfo.output_height, 1, 24, bits);

	// Finish decompression and release memory.
	// I must do it in this order because output module has allocated memory
	// of lifespan JPOOL_IMAGE; it needs to finish before releasing memory.
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	fclose(input_file);

	return TRUE;
}

void CCoverArt::Read(const CString &fileName) {
	if (fileName.IsEmpty())
		return;

	// TODO: decide if it is JPG, PNG, GIF and call appropriate function
	m_bmpCoverArt.DeleteObject();
	GotCoverArt = ReadJpeg(fileName, m_bmpCoverArt);
	Invalidate();
}

